Подробное описание обнаруживаемых учетных данных

Хотя учетные данные FIDO, такие как пароли, направлены на замену паролей, большинство из них также могут освободить пользователя от необходимости вводить имя пользователя. Это позволяет пользователям проходить аутентификацию, выбирая учетную запись из списка паролей, которые у них есть для текущего веб-сайта.

Более ранние версии ключей безопасности были разработаны как методы двухэтапной аутентификации и требовали идентификаторы потенциальных учетных данных, таким образом, требуя ввода имени пользователя. Учетные данные, которые ключ безопасности может найти, не зная их идентификаторов, называются обнаруживаемыми учетными данными. Большинство учетных данных FIDO, созданных сегодня, являются обнаруживаемыми учетными данными; в частности, ключи доступа, хранящиеся в менеджере паролей или на современном ключе безопасности.

Чтобы гарантировать, что ваши учетные данные будут созданы как ключи доступа (обнаруживаемые учетные данные), укажите residentKey и requireResidentKey при создании учетных данных.

Доверяющие стороны (RP) могут использовать обнаруживаемые учетные данные, опуская allowCredentials во время аутентификации ключа доступа. В этих случаях браузер или система показывают пользователю список доступных ключей доступа, идентифицированных свойством user.name , заданным во время создания. Если пользователь выбирает один из них, значение user.id будет включено в результирующую подпись. Затем сервер может использовать его или возвращенный идентификатор учетных данных для поиска учетной записи вместо введенного имени пользователя.

Пользовательские интерфейсы выбора учетных записей, подобные тем, которые обсуждались ранее, никогда не отображают необнаруживаемые учетные данные.

requireResidentKey и residentKey

Чтобы создать ключ доступа, укажите authenticatorSelection.residentKey и authenticatorSelection.requireResidentKey в navigator.credentials.create() со значениями, указанными ниже.

async function register () {
  // ...

  const publicKeyCredentialCreationOptions = {
    // ...
    authenticatorSelection: {
      authenticatorAttachment: 'platform',
      residentKey: 'required',
      requireResidentKey: true,
    }
  };

  const credential = await navigator.credentials.create({
    publicKey: publicKeyCredentialCreationOptions
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;

  // ...
}

residentKey :

  • 'required' : необходимо создать обнаруживаемые учетные данные. Если их невозможно создать, возвращается NotSupportedError .
  • 'preferred' : RP предпочитает создавать обнаруживаемые учетные данные, но принимает необнаруживаемые учетные данные.
  • 'discouraged' : RP предпочитает создавать необнаруживаемые учетные данные, но принимает обнаруживаемые учетные данные.

requireResidentKey :

  • Это свойство сохранено для обратной совместимости с WebAuthn Level 1, более старой версией спецификации. Установите значение true , если residentKey имеет значение 'required' , в противном случае установите значение false .

allowCredentials

RP могут использовать allowCredentials в navigator.credentials.get() для управления опытом аутентификации ключа доступа. Обычно существует три типа опыта аутентификации ключа доступа:

С обнаруживаемыми учетными данными RP могут отображать модальный селектор учетных записей для пользователя, чтобы выбрать учетную запись для входа, с последующей проверкой пользователя. Это подходит для потока аутентификации ключа доступа, инициируемого нажатием кнопки, предназначенной для аутентификации ключа доступа.

Чтобы добиться такого пользовательского опыта, пропустите или передайте пустой массив параметру allowCredentials в navigator.credentials.get() .

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // You can omit `allowCredentials` as well:
    allowCredentials: []
  };

  const credential = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
    signal: abortController.signal
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}

Показать автозаполнение формы ключа доступа

Модальный селектор учетных записей, описанный выше, хорошо работает, если большинство пользователей используют пароли и имеют их на локальном устройстве. Для пользователя, у которого нет локальных паролей, модальный диалог все равно появится и предложит пользователю ввести пароль с другого устройства. При переводе пользователей на пароли вы можете захотеть избежать этого пользовательского интерфейса для пользователей, которые его не настроили.

Вместо этого выбор ключа доступа может быть сложен в запросы на автозаполнение полей в традиционной форме входа, наряду с сохраненными именами пользователей и паролями. Таким образом, пользователь с ключами доступа может «заполнить» форму входа, выбрав свой ключ доступа, пользователи с сохраненными парами имя пользователя/пароль могут выбрать их, а пользователи без них могут по-прежнему вводить свое имя пользователя и пароль.

Такой пользовательский интерфейс идеален, когда RP находится в процессе миграции со смешанным использованием паролей и ключей доступа.

Чтобы добиться такого пользовательского опыта, в дополнение к передаче пустого массива свойству allowCredentials или пропуску параметра укажите mediation: 'conditional' в navigator.credentials.get() и аннотируйте поле ввода username HTML с помощью autocomplete="username webauthn" или поле ввода password с помощью autocomplete="password webauthn" .

Вызов navigator.credentials.get() не приведет к отображению какого-либо пользовательского интерфейса, но если пользователь сфокусируется на аннотированном поле ввода, все доступные ключи доступа будут включены в параметры автозаполнения. Если пользователь выберет один из них, он пройдет обычную проверку разблокировки устройства, и только тогда обещание, возвращенное .get() будет разрешено с результатом. Если пользователь не выберет ключ доступа, обещание никогда не будет разрешено.

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // You can omit `allowCredentials` as well:
    allowCredentials: []
  };

  const cred = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
    signal: abortController.signal,
    // Specify 'conditional' to activate conditional UI
    mediation: 'conditional'
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}
<input type="text" name="username" autocomplete="username webauthn" ...>

Узнать, как создать такой пользовательский интерфейс, можно в статье Вход с использованием ключа доступа с помощью автозаполнения форм , а также в лабораторной работе Внедрение ключей доступа с помощью автозаполнения форм в веб-приложении .

Повторная аутентификация

В некоторых случаях, например, при использовании паролей для повторной аутентификации, идентификатор пользователя уже известен. В этом случае мы хотели бы использовать пароли без отображения браузером или ОС какой-либо формы селектора учетных записей. Этого можно добиться, передав список идентификаторов учетных данных в параметре allowCredentials .

В этом случае, если какие-либо из указанных учетных данных доступны локально, пользователю предлагается немедленно разблокировать устройство. Если нет, пользователю предлагается предоставить другое устройство (телефон или ключ безопасности), содержащее действительные учетные данные.

Чтобы добиться этого пользовательского опыта, предоставьте список идентификаторов учетных данных для пользователя, который входит в систему. RP должен иметь возможность запрашивать их, поскольку пользователь уже известен. Предоставьте идентификаторы учетных данных как объекты PublicKeyCredentialDescriptor в свойстве allowCredentials в navigator.credentials.get() .

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // Provide a list of PublicKeyCredentialDescriptors:
    allowCredentials: [{
      id: ****,
      type: 'public-key',
      transports: [
        'internal',
        'hybrid'
      ]
    }, {
      id: ****,
      type: 'public-key',
      transports: [
        'internal',
        'hybrid'
      ]
    }, ...]
  };

  const credential = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
    signal: abortController.signal
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}

Объект PublicKeyCredentialDescriptor состоит из:

  • id : идентификатор учетных данных открытого ключа, который RP получил при регистрации ключа доступа.
  • type : это поле обычно имеет значение 'public-key' .
  • transports : Подсказка о транспортах, поддерживаемых устройством, содержащим эти учетные данные, используемая браузерами для оптимизации пользовательского интерфейса, который запрашивает у пользователя представление внешнего устройства. Этот список, если он предоставлен, должен содержать результат вызова getTransports() во время регистрации каждого учетного данных.

Краткое содержание

Обнаруживаемые учетные данные делают процесс входа с помощью ключа доступа гораздо более удобным для пользователя, позволяя ему пропустить ввод имени пользователя. Благодаря комбинации residentKey , requireResidentKey и allowCredentials RP могут достичь такого опыта входа, который:

  • Показать модальный селектор аккаунтов.
  • Показать автозаполнение формы ввода ключа доступа.
  • Повторная аутентификация.

Используйте обнаруживаемые учетные данные с умом. Поступая так, вы можете разработать сложные процедуры входа с помощью ключа доступа, которые пользователи найдут без проблем и с которыми с большей вероятностью будут взаимодействовать.